//
// Created by Scave on 2023/11/8.
//

#include "SceneRenderer.h"
#include "bgfx/bgfx.h"
#include "component/BasicComponents.h"
#include "component/InternalComponent.h"
#include "EntityUtil.h"

NS_RIDER_BEGIN

    SceneRenderer::SceneRenderer(const RenderConfig& config) {
        this->render_config_ = config;
        // initialize bgfx
        bgfx::Init init;
#if (defined(FORCE_GLFW))
        init.type = bgfx::RendererType::Enum::OpenGL;
#elif (defined(ANDROID) || defined(HARMONY))
        init.type = bgfx::RendererType::Enum::OpenGLES;
#elif (defined(IOS) || defined(MACOS))
        init.type = bgfx::RendererType::Enum::Metal;
#elif (defined(WINDOWS))
        init.type = bgfx::RendererType::Enum::Direct3D12;
#else
        init.type = bgfx::RendererType::Enum::Count;
#endif
        init.vendorId = BGFX_PCI_ID_NONE;
        bgfx::Resolution resolution;
        resolution.width = config.width;
        resolution.height = config.height;
        resolution.reset = BGFX_RESET_VSYNC;
        init.resolution = resolution;
        bgfx::PlatformData platform_data;
        platform_data.context = nullptr;
        platform_data.backBuffer = nullptr;
        platform_data.backBufferDS = nullptr;
        platform_data.ndt = nullptr;
        if (config.native_window_handle != nullptr) {
            platform_data.nwh = config.native_window_handle;
        }
        init.platformData = platform_data;
        bgfx::init(init);
        bgfx::reset(config.width, config.height, BGFX_RESET_VSYNC, init.resolution.format);
        if (config.debug) {
            bgfx::setDebug(BGFX_DEBUG_TEXT);
        }

        // initialize context
        this->context_ = MAKE_SMART_PTR<RenderContext>(config);
        // initialize render strategies
        this->renderer_sprite_ = MAKE_SMART_PTR<SpriteRenderer>(context_);
        this->renderer_mesh_ = MAKE_SMART_PTR<MeshRenderer>(context_);
    }

    SceneRenderer::~SceneRenderer() {
        context_ = nullptr;
        bgfx::shutdown();
    }

    void SceneRenderer::SetScene(Scene* scene) {
        context_->scene_ = scene;
    }

    TEXTURE_ID_TYPE SceneRenderer::Render() {
        UInt16 root_id = static_cast<UInt16>(LayerMask::Root);
        bgfx::setViewRect(root_id, 0, 0, render_config_.width, render_config_.height);
        bgfx::setViewClear(root_id, BGFX_CLEAR_DEPTH | BGFX_CLEAR_COLOR, 0x000000ff);

        Entity entity = EntityUtil::FindRootEntity(context_->scene_->GetEntityManager());
        auto& data = entity.component<RenderNodes>()->data;
        FOREACH(data, [this](PAIR<const LayerMask, LayerRenderNodes>& pair) {
            this->RenderLayer(pair.first, pair.second);
        });

        if (entity.has_component<DebugInfo>()) {
            ComponentHandle<DebugInfo> debug_info = entity.component<DebugInfo>();
            if (debug_info->text != nullptr) {
                bgfx::touch(root_id);
                bgfx::dbgTextClear();
                bgfx::dbgTextPrintf(debug_info->x, debug_info->y, debug_info->color, debug_info->text);
            }
        }

        bgfx::frame();
        return root_id;
    }

    void SceneRenderer::RenderLayer(LayerMask mask, LayerRenderNodes& layer_nodes) {
        UInt16 layer_id = (UInt16) mask;
        bgfx::setViewRect(layer_id, 0, 0, render_config_.width, render_config_.height);
        bgfx::setViewClear(layer_id, BGFX_CLEAR_DEPTH | BGFX_CLEAR_COLOR, 0x000000ff);
        bgfx::setViewTransform(layer_id, layer_nodes.mtx_view.Data(), layer_nodes.mtx_proj.Data());
        bgfx::touch(layer_id);
        // 给每个Node按order排序
        layer_nodes.Sort([](SMART_PTR<RenderNode>& n1, SMART_PTR<RenderNode>& n2) {
            if (n1->order > n2->order) {
                return 1;
            } else {
                return -1;
            }
        });
        for (auto& node: layer_nodes.nodes) {
            switch (node->Type()) {
                case NodeType::Standard:
                    renderer_mesh_->Render(node);
                    break;
                case NodeType::Skybox:
                    break;
                case NodeType::Particle:
                    break;
                case NodeType::Sprite:
                    renderer_sprite_->Render(node);
                    break;
                case NodeType::Canvas:
                    renderer_canvas_->Render(node);
                    break;
            }
        }
    }

NS_RIDER_END
